﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;
using Token.Inject.Attributes;
using Token.Inject.tag;

namespace Token.Inject
{
    /// <summary>
    /// 注入
    /// </summary>
    public static class AutoInject
    {
        /// <summary>
        /// 主动注入模块
        /// </summary>
        /// <param name="services"></param>
        /// <param name="types">需要注入的程序集</param>
        public static void AddAutoInject(this IServiceCollection services, params Type[] types)
        {
            if (types.Length == 0)
            {
                types = new[] { services.GetType() };
            }

            // 加载所有需要注入的程序集（只有引用的模块）
            var assemblies = types.Select(x => x.Assembly).Distinct()
                .SelectMany(x => x.GetTypes());

            // 过滤程序集
            var where = assemblies
                .Where(type => typeof(ISingletonDependency).IsAssignableFrom(type) ||
                               typeof(IScopedDependency).IsAssignableFrom(type) ||
                               typeof(ITransientDependency).IsAssignableFrom(type));

            // 根据继承的接口注入相对应的生命周期
            foreach (var t in where)
            {
                var interfaces = t.GetDependencyType();

                if (interfaces != null)
                {
                    if (t.IsAssignableFrom<ITransientDependency>())
                    {
                        services.AddTransient(interfaces, t);
                    }
                    else if (t.IsAssignableFrom<IScopedDependency>())
                    {
                        services.AddScoped(interfaces, t);
                    }
                    else if (t.IsAssignableFrom<ISingletonDependency>())
                    {
                        services.AddSingleton(interfaces, t);
                    }
                }
                else
                {
                    if (t.IsAssignableFrom<ITransientDependency>())
                    {
                        services.AddTransient(t);
                    }
                    else if (t.IsAssignableFrom<IScopedDependency>())
                    {
                        services.AddScoped(t);
                    }
                    else if (t.IsAssignableFrom<ISingletonDependency>())
                    {
                        services.AddSingleton(t);
                    }
                }
            }

            where = null;
            assemblies = null;
        }

        static bool IsAssignableFrom<T>(this Type t) =>
            typeof(T).IsAssignableFrom(t);

        /// <summary>
        /// 获取注入方法相对应的需要注入的标记
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static Type? GetDependencyType(this Type type)
        {
            var exposeServices = type.GetCustomAttribute<ExposeServicesAttribute>();

            if (exposeServices == null)
            {
                return type.GetInterfaces().Where(x => x.Name.EndsWith(type.Name))?.FirstOrDefault();
            }

            return type.GetInterfaces().Where(x => x == exposeServices.Type)?.FirstOrDefault();
        }
    }
}